#pragma once
#include <limits>
#include <cstring>
#undef max
#undef min

namespace cppbase
{

template < class NumberType >
struct numeric_limits : public std::numeric_limits<NumberType>
{
private:
	template < bool has_inf > inline static
	NumberType getOverflow(void) { return 0; }

	template < > inline static
	NumberType getOverflow <false>(void) { return max(); }
	template < > inline static
	NumberType getOverflow <true> (void) { return +infinity(); }


	template < bool has_inf > inline static
	NumberType getUnderflow(void) { return 0; }

	template < > inline static
	NumberType getUnderflow <false>(void) { return min(); }
	template < > inline static
	NumberType getUnderflow <true> (void) { return -infinity(); }

public:
	inline static NumberType overflow(void)  { return getOverflow  <has_infinity> (); }
	inline static NumberType underflow(void) { return getUnderflow <has_infinity> (); }
};

template < class BaseNumberType >
struct Number
{
	BaseNumberType number;

	Number(void) : number() {}

	Number(const BaseNumberType & v) : number(v) {}
	Number & operator = (const BaseNumberType & v) { number = v; return *this; }

	template < class NumberType >
	void convertTo(NumberType & output) const
	{
		const static NumberType maximum = numeric_limits<NumberType>::max();
		const static NumberType minimum = numeric_limits<NumberType>::min();
		const static NumberType maxResult = numeric_limits<NumberType>::overflow();
		const static NumberType minResult = numeric_limits<NumberType>::underflow();

		if (number > maximum)
		{
			output = maxResult;
			errno = ERANGE;
		}
		else if (number < minimum)
		{
			output = minResult;
			errno = ERANGE;
		}
		else
		{
			output = static_cast<NumberType>(number);
			if ( number != 0 && output == 0 )
				errno = ERANGE;
		}
	}

	template < >
	void convertTo(BaseNumberType & output) const { output = number; }

};

inline int sz2num(const CHAR  * pStart, INT64  & output, const CHAR  ** ppStop, int base) { Number<INT64>  num =  _strtoi64(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, INT64  & output, const WCHAR ** ppStop, int base) { Number<INT64>  num =  _wcstoi64(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, INT32  & output, const CHAR  ** ppStop, int base) { Number<INT64>  num =  _strtoi64(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, INT32  & output, const WCHAR ** ppStop, int base) { Number<INT64>  num =  _wcstoi64(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, INT16  & output, const CHAR  ** ppStop, int base) { Number<LONG>   num =     strtol(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, INT16  & output, const WCHAR ** ppStop, int base) { Number<LONG>   num =     wcstol(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, INT8   & output, const CHAR  ** ppStop, int base) { Number<LONG>   num =     strtol(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, INT8   & output, const WCHAR ** ppStop, int base) { Number<LONG>   num =     wcstol(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, UINT64 & output, const CHAR  ** ppStop, int base) { Number<UINT64> num = _strtoui64(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, UINT64 & output, const WCHAR ** ppStop, int base) { Number<UINT64> num = _wcstoui64(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, UINT32 & output, const CHAR  ** ppStop, int base) { Number<INT64>  num =  _strtoi64(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, UINT32 & output, const WCHAR ** ppStop, int base) { Number<INT64>  num =  _wcstoi64(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, UINT16 & output, const CHAR  ** ppStop, int base) { Number<LONG>   num =     strtol(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, UINT16 & output, const WCHAR ** ppStop, int base) { Number<LONG>   num =     wcstol(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, UINT8  & output, const CHAR  ** ppStop, int base) { Number<LONG>   num =     strtol(pStart, (CHAR  **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, UINT8  & output, const WCHAR ** ppStop, int base) { Number<LONG>   num =     wcstol(pStart, (WCHAR **)ppStop, base); int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, FLOAT  & output, const CHAR  ** ppStop, int base) { Number<DOUBLE> num =     strtod(pStart, (CHAR  **)ppStop);       int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, FLOAT  & output, const WCHAR ** ppStop, int base) { Number<DOUBLE> num =     wcstod(pStart, (WCHAR **)ppStop);       int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const CHAR  * pStart, DOUBLE & output, const CHAR  ** ppStop, int base) { Number<DOUBLE> num =     strtod(pStart, (CHAR  **)ppStop);       int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }
inline int sz2num(const WCHAR * pStart, DOUBLE & output, const WCHAR ** ppStop, int base) { Number<DOUBLE> num =     wcstod(pStart, (WCHAR **)ppStop);       int ok = (pStart != *ppStop); if (ok) num.convertTo(output); return ok; }


template < class CharType , class NumberType >
inline 
int sztonum(const CharType * pStart, NumberType & output, const CharType ** ppStop = NULL, int base = 0)
{
	errno = 0;
	const CharType * pStop = pStart;
	int ok = sz2num(pStart, output, &pStop, base);
	if ( ppStop ) *ppStop = pStop;
	return ok ? !errno ? 1 : -1 : 0;
}


inline SIZE_T szlen(const CHAR  * string) { return strlen(string); }
inline SIZE_T szlen(const WCHAR * string) { return wcslen(string); }

inline int szcmp(const CHAR  * string1, const CHAR  * string2) { return strcmp(string1, string2); }
inline int szcmp(const WCHAR * string1, const WCHAR * string2) { return wcscmp(string1, string2); }

inline int szncmp(const CHAR  * string1, const CHAR  * string2, SIZE_T length) { return strncmp(string1, string2, length); }
inline int szncmp(const WCHAR * string1, const WCHAR * string2, SIZE_T length) { return wcsncmp(string1, string2, length); }

inline const CHAR *  szstr(const CHAR  * string, const CHAR  * pattern) { return strstr(string, pattern); }
inline const WCHAR * szstr(const WCHAR * string, const WCHAR * pattern) { return wcsstr(string, pattern); }


}
